package common

import (
	"context"
	"database/sql"
	"git.myscrm.cn/golang/stark/v4"
	"os"
	"os/exec"
)

const (
	PrivilegeReadWrite = "ReadWrite"
	PrivilegeReadOnly  = "ReadOnly"
	EmptyDatabaseName  = "modeling_platform_template" //空库名称
)

// CreateDbCmd 命令方式，创建数据库工具类
type CreateDbCmd struct {
	TenantServiceDB
	Host     string
	UserName string
	Password string
}

// NewCreateDbCmd 命令方式，创建数据库
func NewCreateDbCmd() *CreateDbCmd {
	// 从apollo获取
	return &CreateDbCmd{
		Host:     stark.Configer.(stark.ApolloConfigerIface).GetStringValue("StarkMysql.Host", ""),
		UserName: stark.Configer.(stark.ApolloConfigerIface).GetStringValue("mysql_create_tenant_user", ""),
		Password: stark.Configer.(stark.ApolloConfigerIface).GetStringValue("mysql_create_tenant_password", ""),
	}
}

// CreateDatabase 创建数据库
func (r *CreateDbCmd) CreateDatabase(ctx context.Context, dbName string) (string, error) {
	conn, err := sql.Open("mysql", r.UserName+":"+r.Password+"@tcp("+r.Host+":3306)/")
	if err != nil {
		stark.Logger.Errorf(ctx, "开库失败,连接数据库err:%s", err.Error())
		return "", err
	}
	defer conn.Close()

	// 创建数据库
	createDBQuery := "CREATE DATABASE IF NOT EXISTS " + dbName
	_, err = conn.Exec(createDBQuery)
	if err != nil {
		stark.Logger.Errorf(ctx, "开库失败,创建数据库err:%s", err.Error())
		return createDBQuery, err
	}

	return createDBQuery, nil
}

// DbUserInfo 用户信息
type DbUserInfo struct {
	Username  string
	Password  string
	Privilege string
}

// CreateAccount 创建数据库账号 和 授权
func (r *CreateDbCmd) CreateAccount(ctx context.Context, userList []DbUserInfo, dbName string) (string, error) {
	conn, err := sql.Open("mysql", r.UserName+":"+r.Password+"@tcp("+r.Host+":3306)/")
	if err != nil {
		stark.Logger.Errorf(ctx, "开库失败,连接数据库err:%s", err.Error())
		return "", err
	}
	defer conn.Close()

	cmdStr := ""
	// 创建用户账号
	for _, v := range userList {
		createDBQuery := "CREATE USER IF NOT EXISTS '" + v.Username + "'@'%' IDENTIFIED BY '" + v.Password + "';"
		cmdStr += createDBQuery
		_, err = conn.Exec(createDBQuery)
		if err != nil {
			stark.Logger.Errorf(ctx, "开库失败,创建数据库用户账号err:%s", err.Error())
			return cmdStr, err
		}
	}

	//授权
	for _, v := range userList {
		var grantAccountPrivilege string
		if v.Privilege == PrivilegeReadWrite {
			grantAccountPrivilege += "GRANT ALL ON " + dbName + ".* TO '" + v.Username + "'@'%'  WITH GRANT OPTION;"
		} else {
			grantAccountPrivilege += "GRANT SELECT,LOCK TABLES,SHOW VIEW ON " + dbName + ".* TO '" + v.Username + "'@'%'  WITH GRANT OPTION;"
		}
		cmdStr += grantAccountPrivilege
		_, err = conn.Exec(grantAccountPrivilege)
		if err != nil {
			stark.Logger.Errorf(ctx, "开库失败,数据库账号授权err:%s", err.Error())
			return cmdStr, err
		}
	}

	return cmdStr, nil
}

// Backup 备份空库
func (r *CreateDbCmd) Backup(ctx context.Context, emptyDatabase string, dbName string) (string, error) {
	//创建目录
	backupDir := "./runtime/backup/sql/"
	err := r.CreateMutiDir(backupDir)
	if err != nil {
		stark.Logger.Errorf(ctx, "开库失败,创建临时目录err:%s", err.Error())
		return "", err
	}
	//文件名称
	fileName := emptyDatabase + "_" + dbName + ".sql"
	filepath := backupDir + fileName
	isExist := r.isExist(filepath)
	cmdStr := ""

	cmd := exec.Command("mysqldump",
		"-h", r.Host,
		"-u", r.UserName,
		"-p"+r.Password,
		"--no-create-db", //去除创建数据库信息，因为不需要，前面已经创建新数据库了
		emptyDatabase,    //"--databases=modeling_platform_template"  去掉 --databases ，则导出文件不会出现 ：user 数据库
		"--result-file="+filepath,
	)
	cmdStr += cmd.String()
	if isExist == false {
		// 执行命令并等待完成
		_, err := cmd.CombinedOutput()
		if err != nil {
			stark.Logger.Errorf(ctx, "开库失败,备份空库sql err:%s", err.Error())
			return cmdStr, err
		}
	}

	return cmdStr, nil
}

// Recovery 恢复数据库
func (r *CreateDbCmd) Recovery(ctx context.Context, emptyDatabase string, dbName string) (string, error) {
	//文件名称
	backupDir := "./runtime/backup/sql/"
	fileName := emptyDatabase + "_" + dbName + ".sql"
	filepath := backupDir + fileName

	isExist := r.isExist(backupDir)
	cmdStr := ""
	if isExist {
		cmd := exec.Command("mysql",
			"-h", r.Host,
			"-u", r.UserName,
			"-p"+r.Password,
			"--database", dbName,
			"--execute=source "+filepath,
		)
		// 执行命令并等待完成
		cmdStr += cmd.String()
		_, err := cmd.CombinedOutput()
		if err != nil {
			stark.Logger.Errorf(ctx, "开库失败,导入空库数据err:%s", err.Error())
			return cmdStr, err
		}
	}
	return cmdStr, nil
}

// CreateMutiDir 递归创建文件夹
func (r *CreateDbCmd) CreateMutiDir(filePath string) error {
	if !r.isExist(filePath) {
		err := os.MkdirAll(filePath, os.ModePerm)
		if err != nil {
			return err
		}
		return err
	}
	return nil
}

// isExist 判断所给路径文件/文件夹是否存在(返回true是存在)
func (r *CreateDbCmd) isExist(path string) bool {
	_, err := os.Stat(path) //os.Stat获取文件信息
	if err != nil {
		if os.IsExist(err) {
			return true
		}
		return false
	}
	return true
}
